﻿using System;
using System.Collections.Generic;
using System.DirectoryServices;
using System.Linq;
using System.Web;
using Medianamik.Service.ActiveDirectory.DTO;

namespace Medianamik.Service.ActiveDirectory.Service
{
    public class LDAPDomainService : IDomainService
    {
        private readonly string _serverAddress;
        private readonly string _userName;
        private readonly string _password;

        public LDAPDomainService(string serverAddress)
            : this(serverAddress, String.Empty, String.Empty)
        {
        }

        public LDAPDomainService(string serverAddress, string userName, string password)
        {
            _serverAddress = serverAddress.StartsWith("LDAP://") ? serverAddress : "LDAP://" + serverAddress;
            _userName = userName;
            _password = password;
        }

        public bool Exists(string fullObjectPath)
        {
            return DirectoryEntry.Exists(fullObjectPath);
        }

        public bool UserExists(string userName)
        {
            return GetUser(userName) != null;
        }

        public IDomainUser GetUser(string userName)
        {
            if (String.IsNullOrEmpty(userName))
                return new LDAPDomainUser(null);

            SearchResult result;
           
            using (DirectorySearcher searcher = GetDirectorySearcher())
            {
                searcher.Filter = "(&(ObjectClass=user)(sAMAccountName=" + userName.Substring(userName.IndexOf('\\') + 1) + "))";

                // for performance reasons only request needed properties
                searcher.PropertiesToLoad.AddRange(new[] { "sAMAccountName", "sn", "givenName", "mail" });

               result = searcher.FindOne();
            }
            
            return result == null ? new LDAPDomainUser(null) : new LDAPDomainUser(result.GetDirectoryEntry());
        }

        public bool GroupExists(string groupName)
        {
            SearchResult result;

            using (DirectorySearcher searcher = GetDirectorySearcher())
            {
                searcher.Filter = "(&(objectClass=Group)(sAMAccountName=" + groupName + "))";

                // for performance reasons only request needed properties
                searcher.PropertiesToLoad.AddRange(new[] { "sAMAccountName" });

                result = searcher.FindOne();
            }

            return result != null;
        }

        public List<string> GetAllUserNames()
        {
            DirectoryEntry de = GetDirectoryEntry();
            var result = new List<string>();
            using (DirectorySearcher srch = new DirectorySearcher(de, "(objectClass=user)"))
            {
                SearchResultCollection results = srch.FindAll();

                foreach (SearchResult item in results)
                {
                    result.Add(item.Properties["sAMAccountName"][0].ToString());
                }
            }

            return result;
        }

        public List<string> GetAllGroupNames()
        {
            DirectoryEntry de = GetDirectoryEntry();
            
            var result = new List<string>();
            using (DirectorySearcher srch = new DirectorySearcher(de, "(objectClass=Group)"))
            {
                SearchResultCollection results = srch.FindAll();

                foreach (SearchResult item in results)
                    result.Add(item.Properties["sAMAccountName"][0].ToString());

                results.Dispose();
            }

            return result;
        }

        public List<string> GetAllGroupMembers(string groupName)
        {
            var result = new List<string>();
            
            using (var searcher = GetDirectorySearcher())
            {
                searcher.Filter = "(CN=" + groupName + ")";
                var groups = searcher.FindAll();

                foreach (SearchResult group in groups)
                {
                    ResultPropertyCollection props = group.Properties;
                    foreach (object member in props["member"])
                    {
                        var memberEntry = GetDirectoryEntry();
                        memberEntry.Path = _serverAddress + @"/" + member;

                        var userProps = memberEntry.Properties;
                        var userName = userProps["sAMAccountName"].Value;

                        if (userName != null)
                            result.Add(userName.ToString());
                    }
                }
            }

            return result;
        }

        

        private DirectoryEntry GetDirectoryEntry()
        {
            if (string.IsNullOrEmpty(_userName))
                return new DirectoryEntry(_serverAddress);

            return new DirectoryEntry(_serverAddress, _userName, _password, AuthenticationTypes.None);
        }

        private DirectorySearcher GetDirectorySearcher()
        {
            return new DirectorySearcher(GetDirectoryEntry());
        }

        
    }

    public interface IDomainService
    {
        bool Exists(string fullObjectPath);
        bool UserExists(string userName);
        IDomainUser GetUser(string userName);
        bool GroupExists(string groupName);
        List<string> GetAllUserNames();
        List<string> GetAllGroupNames();
        List<string> GetAllGroupMembers(string groupName);
    }
}